/** @noSelfInFile **/
import {OrderId} from "../../../../type/Type";
import {UnitDataKey, UnitSourceData} from "../../../dataSource/DataSource";
import {AttributeEnum, HurtType, UnitData} from "../../../dataSource/DataModel";
import Configer from "../../config/Configer";
import {LocModel} from "../../base/LocModel";
import {UnitJson} from "../../model/StoreJson";
import {CodeUtil} from "../../../codeAdapter/CodeUtil";
import {DataRouter} from "../../../dataSource/DataRouter";
import {FinalKey} from "../../../../constant/FinalConstant";
import {HandleModel} from "../model1/HandleModel";
import {GlobalModel} from "../model1/GlobalModel";
import {IUnitAdapter, unitAdapter} from "../../adapter/adapterInterface/model3/IUnitAdapter";
import {ItemModel} from "./ItemModel";
import {ConfigKey} from "../../../../constant/ConfigConstant";
import {Logger} from "../../config/Logger";


export class UnitModel implements HandleModel {

    public data: UnitSourceData = new UnitSourceData();
    public template: UnitData;

    unitAdapter: IUnitAdapter;

    public handle: any;
    id: string;
    nikeName: string;

    constructor(name: string) {
        this.nikeName = name;
        this.unitAdapter = unitAdapter.adapter
    }

    createHandle(owner: number, unitJson: UnitJson, template: UnitData, loc: LocModel, face: number) {
        const unit = this.unitAdapter.createHandle(owner, unitJson.id, loc, face);
        this.bindHandle(unit, template)
        this.loadJson(unitJson);
    }

    public set acquireRange(value: number) {
        this.unitAdapter.setAcquireRange(this.handle, value);
    }


    public isAliveInBattle() {
        if (this.isAlive() && !CodeUtil.isNullOrZero(this.data[UnitDataKey.bBattle])) {
            return true
        }
        return false;
    }

    public isAlive() {
        const bDead = this.data[UnitDataKey.bDead];
        if (bDead === 0 && this.isValid()) {
            return true;
        }
        return false;
    }

    public get acquireRange() {
        return this.unitAdapter.getAcquireRange(this.handle);
    }

    public set canSleep(flag: boolean) {
        this.unitAdapter.setCanSleep(this.handle, flag);
    }

    public get canSleep() {
        return this.unitAdapter.canSleepPerm(this.handle);
    }

    public get currentOrder() {
        return this.unitAdapter.getCurrentOrder(this.handle);
    }

    public get defaultAcquireRange() {
        return this.unitAdapter.getDefaultAcquireRange(this.handle);
    }

    public get defaultFlyHeight() {
        return this.unitAdapter.getDefaultFlyHeight(this.handle);
    }

    public get defaultMoveSpeed() {
        return this.unitAdapter.getDefaultMoveSpeed(this.handle);
    }

    public get defaultPropWindow() {
        return this.unitAdapter.getDefaultPropWindow(this.handle);
    }

    public get defaultTurnSpeed() {
        return this.unitAdapter.getDefaultTurnSpeed(this.handle);
    }

    public get experience() {
        return this.unitAdapter.getExperience(this.handle);
    }

    public setExperience(newXpVal: number, showEyeCandy: boolean = true) {
        this.unitAdapter.setExperience(this.handle, newXpVal, showEyeCandy);
    }

    public set facing(value: number) {
        this.unitAdapter.setFacing(this.handle, value);
    }

    public get facing() {
        return this.unitAdapter.getFacing(this.handle);
    }

    public get ignoreAlarmToggled() {
        return this.unitAdapter.getIgnoreAlarmToggled(this.handle);
    }

    public get inventorySize() {
        return this.unitAdapter.getInventorySize(this.handle);
    }

    public set invulnerable(flag: boolean) {
        const invulnerableSkill = Configer.getFinal(FinalKey.invulnerableSkill);
        if (flag) {
            this.addAbility(invulnerableSkill);
        } else {
            this.removeAbility(invulnerableSkill);
        }
    }

    public get invulnerable() {
        const invulnerableSkill = Configer.getFinal(FinalKey.invulnerableSkill);
        const has = this.hasAbility(invulnerableSkill);
        return has;
    }

    public get level() {
        return this.unitAdapter.getLevel(this.handle);
    }
    public set level(value: number) {
        this.unitAdapter.setLevel(this.handle, value);
    }
    get name() {
        return this.unitAdapter.getName(this.handle);
    }

    public set paused(flag: boolean) {
        this.unitAdapter.setPaused(this.handle, flag);
    }

    public get paused() {
        return this.unitAdapter.getPaused(this.handle);
    }

    public get loc() {
        return this.unitAdapter.getLoc(this.handle);
    }

    public set loc(locModel: LocModel) {
        this.unitAdapter.setLoc(this.handle, locModel);
    }

    public set propWindow(value: number) {
        this.unitAdapter.setPropWindow(this.handle, value);
    }

    public get propWindow() {
        return this.unitAdapter.getPropWindow(this.handle);
    }

    public set show(flag: boolean) {
        this.unitAdapter.setShow(this.handle, flag);
    }

    public get show() {
        return this.unitAdapter.getShow(this.handle);
    }

    public get sleeping() {
        return this.unitAdapter.getSleeping(this.handle);
    }

    public set turnSpeed(value: number) {
        this.unitAdapter.setTurnSpeed(this.handle, value);
    }

    public get turnSpeed() {
        return this.unitAdapter.getTurnSpeed(this.handle);
    }

    public get strId() {
        return this.unitAdapter.getStrId(this.handle);
    }

    public get life() {
        if (this.handle) {
            return this.unitAdapter.getLife(this.handle);
        } else {
            return 0;
        }
    }

    public set life(value: number) {
        this.unitAdapter.setLife(this.handle, value)
        if (value > 0) {
        } else {
            this.kill();
        }
    }

    public get maxLife() {
        return this.unitAdapter.getMaxLife( this.handle);
    }

    public set maxLife(value: number) {
        this.unitAdapter.setMaxLife(this.handle, value);
    }

    public get lifePercent() {
        return this.unitAdapter.getLifePercent(this.handle);
    }

    public set lifePercent(value: number) {
        this.unitAdapter.setLifePercent(this.handle, value)
    }

    public get mana() {
        return this.unitAdapter.getMana(this.handle)
    }

    public set mana(value: number) {
        this.unitAdapter.setMana(this.handle, value)
    }

    public get maxMana() {
        return this.unitAdapter.getMaxMana(this.handle);
    }

    public set maxMana(value: number) {
        this.unitAdapter.setMaxMana(this.handle, value);
    }

    public get manaPercent() {
        return this.unitAdapter.getManaPercent(this.handle);
    }

    public set manaPercent(value: number) {
        this.unitAdapter.setManaPercent(this.handle, value)
    }

    public get attackSpeed() {
        return this.unitAdapter.getAttackSpeed(this.handle)
    }

    public get attackSpeedInterval() {
        return this.unitAdapter.getAttackSpeedInterval(this.handle);
    }

    public set attackSpeedInterval(value: number) {
        this.unitAdapter.setAttackSpeedInterval(this.handle, value);
    }

    public set attackSpeed(value: number) {
        this.unitAdapter.setAttackSpeed(this.handle, value);
    }

    public set moveSpeed(value: number) {
        this.unitAdapter.setMoveSpeed(this.handle, value)
    }

    public get moveSpeed() {
        return this.unitAdapter.getMoveSpeed(this.handle);
    }

    public get attackDistance() {
        return this.unitAdapter.getAttackDistance(this.handle)
    }

    public set attackDistance(value: number) {
        this.unitAdapter.setAttackDistance(this.handle, value);
    }

    public setModelPath(resourceId: string, bAlways: boolean = false) {
        const resourceData = DataRouter.getResourceDataByResourceId(resourceId);
        const path = resourceData?.src
        if (path) {
            if (bAlways) {
                this.data[UnitDataKey.modelDefaultPath] = resourceId
            }
            this.data[UnitDataKey.modelPath] = resourceId;
            this.unitAdapter.setModelPath(this.handle, path);
        }
    }


    public getModelPath() {
        return this.data[UnitDataKey.modelPath];
    }

    public getIconPath() {
        return this.data[UnitDataKey.iconPath];
    }

    public replaceAbility(oldAbilityId: string, newAbilityId: string) {
        if (this.hasAbility(oldAbilityId)) {
            this.removeAbility(oldAbilityId);
            this.addAbility(newAbilityId);
        }
    }

    public addAbility(abilityId: number | string) {
        this.unitAdapter.addAbility(this.handle, abilityId);
    }

    public addSleepPerm(add: boolean) {
        this.unitAdapter.addSleepPerm(this.handle, add);
    }

    public applyTimedLife(buffId: number | string, duration: number) {
        this.unitAdapter.applyTimedLife(this.handle, buffId, duration);
    }

    public canSleepPerm() {
        return this.unitAdapter.canSleepPerm(this.handle);
    }

    public decAbilityLevel(abilCode: number) {
        return this.unitAdapter.decAbilityLevel(this.handle, abilCode);
    }



    public hasAbility(abilId: string): boolean {
        return this.unitAdapter.hasAbility(this.handle, abilId);
    }

    public getAbilityLevel(abilCode: string) {
        return this.unitAdapter.getAbilityLevel(this.handle, abilCode)
    }
    public getAbilitysLevel(abilCode: string) {
        const abilitys = this.data[UnitDataKey.abilitys];
        let num = 0
        CodeUtil.mapObj(abilitys, (key, value) => {
            if (value.id === abilCode) {
                num = value.lv;
                return true;
            }
        })
        return num;
    }


    public getAgility(includeBonuses: boolean) {
        return this.unitAdapter.getAgility(this.handle, includeBonuses);
    }

    public getflyHeight() {
        return this.unitAdapter.getflyHeight(this.handle);
    }

    public getIgnoreAlarm(flag: boolean) {
        return this.unitAdapter.getIgnoreAlarm(this.handle, flag);
    }

    public getIntelligence(includeBonuses: boolean) {
        return this.unitAdapter.getIntelligence(this.handle, includeBonuses);
    }

    public getStrength(includeBonuses: boolean) {
        return this.unitAdapter.getStrength(this.handle, includeBonuses);
    }

    public isHero() {
        return this.unitAdapter.isHero(this.handle);
    }

    public issueBuildOrder(id: string, loc: LocModel) {
        return this.unitAdapter.issueBuildOrder(this.handle, id, loc);
    }

    public issueImmediateOrder(order: string | OrderId) {
        return this.unitAdapter.issueImmediateOrder(this.handle, order);
    }


    public issueOrderAtWithEffect(order: string | OrderId, loc: LocModel) {
        return this.unitAdapter.issueOrderAt(this.handle, order, loc);
    }

    public issueOrderAt(order: string | OrderId, loc: LocModel) {
        return this.unitAdapter.issueOrderAt(this.handle, order, loc);
    }


    public issueTargetOrder(order: string | OrderId, targetWidget: UnitModel) {
        return this.unitAdapter.issueTargetOrder(this.handle, order, targetWidget.handle);
    }

    /**
     * Kills the unit.
     */
    public kill() {
        this.unitAdapter.kill(this.handle);
        this.dead();
    }

    // public pauseEx(flag: boolean) {
    //     BlzPauseUnitEx(this.handle, flag);
    // }

    public pauseTimedLife(flag: boolean) {
        this.unitAdapter.pauseTimedLife(this.handle, flag);
    }

    public recycleGuardPosition() {
        this.unitAdapter.recycleGuardPosition(this.handle);
    }

    public removeAbility(abilityId: number | string) {
        return this.unitAdapter.removeAbility(this.handle, abilityId);
    }

    public removeGuardPosition() {
        this.unitAdapter.removeGuardPosition(this.handle);
    }

    public resetCooldown() {
        this.unitAdapter.resetCooldown(this.handle);
    }

    public revive(loc: LocModel, doEyecandy: boolean) {
        this.data[UnitDataKey.bDead] = 0;
        const ret = this.unitAdapter.revive(this.handle, loc, doEyecandy);
        return ret;
    }

    public dead() {
        this.data[UnitDataKey.bDead] = 1;
    }

    public select(flag: boolean) {
        this.unitAdapter.select(this.handle, flag);
    }

    public setAbilityLevel(abilCode: number | string, level: number) {
        this.unitAdapter.setAbilityLevel(this.handle, abilCode, level);
    }


    public setAgility(value: number, permanent: boolean) {
        this.unitAdapter.setAgility(this.handle, value, permanent);
    }

    public setAnimation(whichAnimation: string) {
        this.unitAdapter.setAnimation(this.handle, whichAnimation);
    }

    public setBlendTime(timeScale: number) {
        this.unitAdapter.setBlendTime(this.handle, timeScale);
    }

    public setCreepGuard(creepGuard: boolean) {
        this.unitAdapter.setCreepGuard(this.handle, creepGuard);
    }



    public setUnitIcon(resourceId: string) {
        const resourceData = DataRouter.getResourceDataByResourceId(resourceId);
        const path = resourceData?.src
        if (path) {
            this.data[UnitDataKey.iconPath] = resourceId;
            this.unitAdapter.setUnitIcon(this.handle, path);
        }
    }

    public setflyHeight(value: number, rate: number) {
        this.unitAdapter.setflyHeight(this.handle, value, rate);
    }

    public setIntelligence(value: number, permanent: boolean) {
        this.unitAdapter.setIntelligence(this.handle, value, permanent);
    }

    public setPathing(flag: boolean) {
        this.unitAdapter.setPathing(this.handle, flag);
    }


    public setScale(scaleX: number, scaleY: number, scaleZ: number) {
        this.unitAdapter.setScale(this.handle, scaleX, scaleY, scaleZ);
    }
    public setStrength(value: number, permanent: boolean) {
        this.unitAdapter.setStrength(this.handle, value, permanent);
    }

    public setTimeScale(timeScale: number) {
        this.unitAdapter.setTimeScale(this.handle, timeScale);
    }

    public setUseAltIcon(flag: boolean) {
        this.unitAdapter.setUseAltIcon(this.handle, flag);
    }

    public setUseFood(useFood: boolean) {
        this.unitAdapter.setUseFood(this.handle, useFood);
    }

    public setVertexColor(red: number, green: number, blue: number, alpha: number) {
        this.unitAdapter.setVertexColor(this.handle, red, green, blue, alpha);
    }

    public wakeUp() {
        this.unitAdapter.wakeUp(this.handle);
    }

    public getJson() {
        const json: UnitJson = { id: this.strId, e: this.experience };
        return json
    }
    public loadJson(json: UnitJson) {
        if (!CodeUtil.isNullOrZero(json.e)) {
            this.data[UnitDataKey.maxLvCache] = 999;
            this.setExperience(json.e, false);
            this.data[UnitDataKey.maxLvCache] = this.level;
            this.data[UnitDataKey.maxLvHistory] = this.level;
        }
        if (this.template) {
            if (this.template.modelPath) {
                this.setModelPath(this.template.modelPath, true)
            } else {
                this.setModelPath(this.template.file, true)
            }
            if (this.template.iconPath) {
                this.setUnitIcon(this.template.iconPath)
            }
        }
    }


    // 修改单位绿字的接口
    public setExtraAttr(val: number,  targetAttr: AttributeEnum) {
        this.unitAdapter.setExtraAttr(this.handle, val, targetAttr);
    }

    destroy() {
        this.unitAdapter.destroy(this.handle);
        this.handle = undefined;
    }

    bindHandle(handle: any, template?: UnitData) {
        this.unitAdapter.bindHandle(handle)
        this.template = template;
        this.handle = handle;
        this.id = GlobalModel.getHandleId(this.handle);
    }

    equal(handle: UnitModel): boolean {
        return this.unitAdapter.equal(this.handle, handle.handle)
    }

    isVertigo() {
        return this.unitAdapter.isVertigo(this.handle)
    }

    getHandleId(): string {
        return this.id;
    }

    damage(unitModel: UnitModel, hurtValue: number, hurtType: HurtType): void {
        this.unitAdapter.damage(this.handle, unitModel.handle, hurtValue, hurtType);
    }

    isValid(): boolean {
        return  this.unitAdapter.isValid(this.handle);
    }

    getOwnerId(): number {
        return this.unitAdapter.getOwnerId(this.handle);
    }

    useAbility(id: string, targetLoc?: LocModel, targetUnit?: UnitModel) {
        return this.unitAdapter.useAbility(this.handle, id, targetLoc, targetUnit?.handle);
    }

    getFirstItem(): ItemModel {
        const index= this.data[UnitDataKey.unitBagsIndex];
        return this.data[UnitDataKey.unitBag][index][0]
    }
    // todo 【需要被回退】
    giveItem(item: ItemModel, targetIndex?: number): boolean {
        let ret = this.unitAdapter.giveItem(this.handle, item.handle, targetIndex);
        return ret;
        // const index = this.data[UnitDataKey.unitBagsIndex];
        // const maxItemNum = Configer.getConfig(ConfigKey.maxItemSlot);
        // const bag = this.data[UnitDataKey.unitBag][index];
        // let success = false;
        // if (targetIndex) {
        //     let oldIndex = -1;
        //     for (let i = 0; i < maxItemNum; i++) {
        //         const tempItem = bag[i];
        //         if (tempItem === item) {
        //             oldIndex = i;
        //             break;
        //         }
        //     }
        //     if (oldIndex >= 0) {
        //         const tempItem = bag[targetIndex];
        //         bag[targetIndex] = item;
        //         bag[oldIndex] = tempItem;
        //         success = true;
        //     }
        // } else {
        //     if (bag.length >= maxItemNum) {
        //         return false;
        //     }
        //     bag.push(item);
        //     success = true
        // }
        // if (success) {
        //     item.giveToUnit(this);
        // }
    }

    clearItem(item: ItemModel) {
        const index = this.data[UnitDataKey.unitBagsIndex];
        const maxItemNum = Configer.getConfig(ConfigKey.maxItemSlot);
        const bag = this.data[UnitDataKey.unitBag][index];

        let oldIndex = -1;
        for (let i = 0; i < maxItemNum; i++) {
            const tempItem = bag[i];
            if (tempItem === item) {
                oldIndex = i;
                break;
            }
        }
        if (oldIndex >= 0) {
            bag[oldIndex] = undefined;
            return true;
        }
    }

    dropItem(item: ItemModel, loc: LocModel) {
        const ret = this.clearItem(item);
        if (ret) {
            item.dropToLoc(loc);
        }
    }

    mapItems(callback: (item: ItemModel, index: number) => boolean) {
        const index = this.data[UnitDataKey.unitBagsIndex];
        const maxItemNum = Configer.getConfig(ConfigKey.maxItemSlot);
        const bag = this.data[UnitDataKey.unitBag][index];
        for (let i = 0; i < maxItemNum; i++) {
            const item = bag[i];
            if (item) {
                const ret = callback(item, i);
                if (ret) {
                    break;
                }
            }
        }
    }

    mapEquips(callback: (item: ItemModel, index: number) => boolean) {
        const maxItemNum = Configer.getConfig(ConfigKey.maxGoodsEquipNum);
        const bag = this.data[UnitDataKey.unitEquip];
        for (let i = 0; i < maxItemNum; i++) {
            const item = bag[i];
            if (item) {
                const ret = callback(item, i);
                if (ret) {
                    break;
                }
            }
        }
    }

}
